using System;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;
using UnityEngine.Experimental.Rendering;
using UnityEngine.Rendering;

namespace Mars.ShaderAnalysis
{
    public class ReflectionFrameDebuggerEventData
    {
        public object data;
        public Shader shader;
        public int frameEventIndex = -1;
        public string passName = "";
        public string passLightMode = "";
        public int subShaderIndex = -1;
        public int shaderPassIndex = -1;
        public string shaderKeywords = "";
        public string keywordString = "";
        public string hashString = "";
        public int drawCallCount = 0;

        public string rtName;
        public GraphicsFormat rtFormat;
        public short rtCount;
        public bool rtHasDepthTexture;
        public int rtWidth;
        public int rtHeight;

        public RenderBufferLoadAction rtLoadAction;
        public RenderBufferStoreAction rtStoreAction;
        public RenderBufferLoadAction rtDepthLoadAction;
        public RenderBufferStoreAction rtDepthStoreAction;
        public RenderTexture m_RenderTargetRenderTexture;
        public string frameEventType;
        public bool isDepthRt = false;
        public int rtIndex = 0;
        public int bits = -1;

        public ReflectionFrameDebuggerEventData()
        {
            Assembly assembly = Assembly.Load("UnityEditor.CoreModule");
            Type FrameDebuggerEventData = assembly.GetType("UnityEditorInternal.FrameDebuggerInternal.FrameDebuggerEventData");
            data = Activator.CreateInstance(FrameDebuggerEventData);
        }

        /// <summary>
        /// 分析
        /// </summary>
        public void Analysis(int rtIndex)
        {
            Assembly assembly = Assembly.Load("UnityEditor.CoreModule");
            Type FrameDebuggerEventData = assembly.GetType("UnityEditorInternal.FrameDebuggerInternal.FrameDebuggerEventData");
            string shaderName = FrameDebuggerEventData.GetField("m_OriginalShaderName", BindingFlags.Instance | BindingFlags.Public).GetValue(data) as string;
            shader = Shader.Find(shaderName);

            frameEventIndex = (int)FrameDebuggerEventData.GetField("m_FrameEventIndex", BindingFlags.Instance | BindingFlags.Public).GetValue(data) + 1;
            drawCallCount = (int)FrameDebuggerEventData.GetField("m_DrawCallCount", BindingFlags.Instance | BindingFlags.Public).GetValue(data);

            //Shader相关
            passName = FrameDebuggerEventData.GetField("m_PassName", BindingFlags.Instance | BindingFlags.Public).GetValue(data) as string;
            passLightMode = FrameDebuggerEventData.GetField("m_PassLightMode", BindingFlags.Instance | BindingFlags.Public).GetValue(data) as string;
            subShaderIndex = (int)FrameDebuggerEventData.GetField("m_SubShaderIndex", BindingFlags.Instance | BindingFlags.Public).GetValue(data);
            shaderPassIndex = (int)FrameDebuggerEventData.GetField("m_ShaderPassIndex", BindingFlags.Instance | BindingFlags.Public).GetValue(data);

            //变体
            object m_ShaderInfo = FrameDebuggerEventData.GetField("m_ShaderInfo", BindingFlags.Instance | BindingFlags.Public).GetValue(data);
            Type ShaderInfo = assembly.GetType("UnityEditorInternal.FrameDebuggerInternal.ShaderInfo");
            Array m_Keywords = ShaderInfo.GetField("m_Keywords", BindingFlags.Instance | BindingFlags.Public).GetValue(m_ShaderInfo) as Array;
            Type ShaderKeywordInfo = assembly.GetType("UnityEditorInternal.FrameDebuggerInternal.ShaderKeywordInfo");
            shaderKeywords = "";
            for (int i = 0; i < m_Keywords.Length; i++)
            {
                string m_Name = ShaderKeywordInfo.GetField("m_Name", BindingFlags.Instance | BindingFlags.Public).GetValue(m_Keywords.GetValue(i)) as string;
                shaderKeywords += m_Name;
                if (i < m_Keywords.Length - 1)
                {
                    shaderKeywords += " ";
                }
            }

            //RT相关
            rtName = FrameDebuggerEventData.GetField("m_RenderTargetName", BindingFlags.Instance | BindingFlags.Public).GetValue(data) as string;
            rtFormat = (GraphicsFormat)(int)FrameDebuggerEventData.GetField("m_RenderTargetFormat", BindingFlags.Instance | BindingFlags.Public).GetValue(data);
            rtCount = (short)FrameDebuggerEventData.GetField("m_RenderTargetCount", BindingFlags.Instance | BindingFlags.Public).GetValue(data);
            rtWidth = (int)FrameDebuggerEventData.GetField("m_RenderTargetWidth", BindingFlags.Instance | BindingFlags.Public).GetValue(data);
            rtHeight = (int)FrameDebuggerEventData.GetField("m_RenderTargetHeight", BindingFlags.Instance | BindingFlags.Public).GetValue(data);
            rtHasDepthTexture = (sbyte)FrameDebuggerEventData.GetField("m_RenderTargetHasDepthTexture", BindingFlags.Instance | BindingFlags.Public).GetValue(data) != (sbyte)0;
            if (rtHasDepthTexture)
                ++rtCount;

            //有时候深度rt获取到的action为-1，这里需要处理下
            this.rtIndex = rtIndex;
            isDepthRt = rtIndex == rtCount - 1 && rtHasDepthTexture;

            rtLoadAction = (RenderBufferLoadAction)(int)FrameDebuggerEventData.GetField("m_RenderTargetLoadAction", BindingFlags.Instance | BindingFlags.Public).GetValue(data);
            rtStoreAction = (RenderBufferStoreAction)(int)FrameDebuggerEventData.GetField("m_RenderTargetStoreAction", BindingFlags.Instance | BindingFlags.Public).GetValue(data);
            rtDepthLoadAction = (RenderBufferLoadAction)(int)FrameDebuggerEventData.GetField("m_RenderTargetDepthLoadAction", BindingFlags.Instance | BindingFlags.Public).GetValue(data);
            rtDepthStoreAction = (RenderBufferStoreAction)(int)FrameDebuggerEventData.GetField("m_RenderTargetDepthStoreAction", BindingFlags.Instance | BindingFlags.Public).GetValue(data);
            if (shader != null)
            {
                keywordString = string.Format("{0}, SubShader #{1}\n{2}", shader.name, subShaderIndex.ToString(), shaderKeywords);
                hashString = string.Format("SubShader #{0}, {1}{2}\n{3}", subShaderIndex.ToString(), passName,
                    string.IsNullOrEmpty(passLightMode) ? "" : (" (" + passLightMode + ")"), shaderKeywords);
            }

            m_RenderTargetRenderTexture = (RenderTexture)FrameDebuggerEventData.GetField("m_RenderTargetRenderTexture", BindingFlags.Instance | BindingFlags.Public).GetValue(data);
            if (bits < 0)
            {
                bits = GraphicsUtility.GetGraphicsFormatBits(rtFormat);
                if (bits == 0)
                {
                    if (m_RenderTargetRenderTexture != null)
                    {
                        bits = m_RenderTargetRenderTexture.depth;
                    }
                }
            }

            //SetPassCall类型
            Type FrameDebuggerUtility = assembly.GetType("UnityEditorInternal.FrameDebuggerInternal.FrameDebuggerUtility");
            MethodInfo GetFrameEvents = FrameDebuggerUtility.GetMethod("GetFrameEvents", BindingFlags.Static | BindingFlags.Public);
            Type FrameDebuggerEvent = assembly.GetType("UnityEditorInternal.FrameDebuggerInternal.FrameDebuggerEvent");
            Array frameEvents = GetFrameEvents.Invoke(null, null) as Array;
            object frameDebuggerEvent = frameEvents.GetValue(frameEventIndex - 1);
            frameEventType = FrameDebuggerEvent.GetField("m_Type", BindingFlags.Instance | BindingFlags.Public).GetValue(frameDebuggerEvent).ToString();
        }

        public double bandWidth
        {
            get
            {
                if (rtName == "<No name>")
                {
                    return 0;
                }

                double _bandWidth = 0;
                double size = rtWidth * rtHeight;
                size *= bits;
                size /= 8;
                //以每秒30帧算
                size *= 30;
                if (!isDepthRt)
                {
                    if (rtLoadAction == RenderBufferLoadAction.Load)
                    {
                        _bandWidth += size;
                    }

                    if (rtStoreAction == RenderBufferStoreAction.Store)
                    {
                        _bandWidth += size;
                    }
                }
                else
                {
                    if (rtDepthLoadAction == RenderBufferLoadAction.Load)
                    {
                        _bandWidth += size;
                    }

                    if (rtDepthStoreAction == RenderBufferStoreAction.Store)
                    {
                        _bandWidth += size;
                    }
                }

                return _bandWidth;
            }
        }

        public string bandWidthString
        {
            get
            {
                double _bandWidth = bandWidth;
                string _bandWidthString = (_bandWidth / (1024 * 1024 * 1024)).ToString("0.00") + " GiB/s";
                if (_bandWidth < 1024)
                {
                    _bandWidthString = _bandWidth.ToString("0.00") + " B/s";
                }
                else if (_bandWidth < 1024 * 1024)
                {
                    _bandWidthString = (_bandWidth / 1024).ToString("0.00") + " KiB/s";
                }
                else if (_bandWidth < 1024 * 1024 * 1024)
                {
                    _bandWidthString = (_bandWidth / (1024 * 1024)).ToString("0.00") + " MiB/s";
                }

                return _bandWidthString;
            }
        }

        public string formatString
        {
            get { return rtFormat + " (" + (bits > 0 ? bits : "??") + " bits)"; }
        }
    }

    public class FrameDebuggerEventDataList
    {
        public bool isFoldout = false;
        public string hashString = "";
        public string keywordString = "";
        public List<ReflectionFrameDebuggerEventData> dataList = new List<ReflectionFrameDebuggerEventData>();

        public int setPassCalls
        {
            get { return dataList.Count; }
        }

        public int drawCalls
        {
            get
            {
                int _drawCalls = 0;
                for (int i = 0; i < dataList.Count; i++)
                {
                    _drawCalls += dataList[i].drawCallCount;
                }

                return _drawCalls;
            }
        }
    }

    public class FrameDebuggerEventDataGroup
    {
        public bool isFoldout = false;
        public Dictionary<string, FrameDebuggerEventDataList> dataDic = new Dictionary<string, FrameDebuggerEventDataList>();

        public int setPassCalls
        {
            get
            {
                int _setPassCalls = 0;
                foreach (var kv in dataDic)
                {
                    _setPassCalls += kv.Value.setPassCalls;
                }

                return _setPassCalls;
            }
        }

        public int drawCalls
        {
            get
            {
                int _drawCalls = 0;
                foreach (var kv in dataDic)
                {
                    _drawCalls += kv.Value.drawCalls;
                }

                return _drawCalls;
            }
        }
    }

    public class FrameDebuggerEventRtList
    {
        public bool isFoldout = false;
        public int frameEventIndex = -1;
        public List<ReflectionFrameDebuggerEventData> dataList = new List<ReflectionFrameDebuggerEventData>();

        public string bandWidthString
        {
            get
            {
                double _bandWidth = bandWidth;
                string _bandWidthString = (_bandWidth / (1024 * 1024 * 1024)).ToString("0.00") + " GiB/s";
                if (_bandWidth < 1024)
                {
                    _bandWidthString = _bandWidth.ToString("0.00") + " B/s";
                }
                else if (_bandWidth < 1024 * 1024)
                {
                    _bandWidthString = (_bandWidth / 1024).ToString("0.00") + " KiB/s";
                }
                else if (_bandWidth < 1024 * 1024 * 1024)
                {
                    _bandWidthString = (_bandWidth / (1024 * 1024)).ToString("0.00") + " MiB/s";
                }

                return _bandWidthString;
            }
        }


        public double bandWidth
        {
            get
            {
                double _bandWidth = 0;
                HashSet<string> rtHash = new HashSet<string>();
                for (int i = 0; i < dataList.Count; i++)
                {
                    ReflectionFrameDebuggerEventData data = dataList[i];
                    //查重，防止重复计算相同rt
                    if (rtHash.Contains(data.rtName))
                    {
                        continue;
                    }

                    rtHash.Add(data.rtName);
                    _bandWidth += data.bandWidth;
                }

                //以每秒30帧算
                return _bandWidth;
            }
        }
    }

    public class FrameDebuggerEventRtGroup
    {
        public bool isFoldout = false;
        public List<FrameDebuggerEventRtList> rtList = new List<FrameDebuggerEventRtList>();

        public void Clear()
        {
            isFoldout = false;
            rtList.Clear();
        }

        public FrameDebuggerEventRtList last
        {
            get
            {
                if (rtList.Count > 0)
                {
                    return rtList[rtList.Count - 1];
                }

                return null;
            }
        }
    }
}